<?php
class Pageley_Gateway_Database_StandardQuerySqlDecoratorTests extends Pageley_Test_Case {
	private $articleType;
	private $keywordType;
	private $tagType;
	private $categoryType;
	private $eagerType;
	private $gateway;

	public function setUp() {
		$string = new Pageley_Domain_Attribute_Basic("string");
		$number = new Pageley_Domain_Attribute_Basic("number");
        $namespace = new Pageley_Domain_BasicNamespace("urn:gateway.pageley.org:unit-tests", "mysql", "Pageley_Commerce");
		$this->categoryType = new Pageley_Domain_BasicType(
			$namespace,
			"Category"
		);
        $this->keywordType = new Pageley_Domain_BasicType(
			$namespace,
			"Keyword"
		);
        $this->tagType = new Pageley_Domain_BasicType(
            $namespace,
            "Tag"
        );
        $category = new Pageley_Domain_Attribute_Reference("category", $this->categoryType);
        $this->categoryType->setAttributes(
			new Pageley_Util_ArrayMap(array(
				"name" => $string,
                "parentCategory" => $category
			)));
        $keywords = new Pageley_Domain_Attribute_OneToManyDecorator(
            new Pageley_Domain_Attribute_Reference("keyword", $this->keywordType));
        $tags = new Pageley_Domain_Attribute_ManyToManyDecorator(
            new Pageley_Domain_Attribute_Reference("tag", $this->tagType));
		$this->articleType = new Pageley_Domain_BasicType(
			$namespace,
			"Article",
			new Pageley_Util_ArrayMap(array(
				"name" => $string,
				"price" => $number,
                "category" => $category,
                "keywords" => $keywords,
			    "tags" => $tags
			))
		);
		$article = new Pageley_Domain_Attribute_Reference("article", $this->articleType);
		$this->keywordType->setAttributes(
			new Pageley_Util_ArrayMap(array(
				"name" => $string,
				"article" => $article
			)));
	    $this->tagType->setAttributes(
	    new Pageley_Util_ArrayMap(array(
                "name" => $string
            )));
        $eagerCategory = new Pageley_Domain_Attribute_Reference("category", $this->categoryType);
        $eagerCategory->setEager(true);
		$this->eagerType = new Pageley_Domain_BasicType(
            $namespace,
            "Eager",
            new Pageley_Util_ArrayMap(array(
                "category" => $eagerCategory
            ))
        );
		$this->gateway = new Pageley_Gateway_Database(null);
	}

	private function newQuery($type, $condition = null, $order = null, $range = null) {
		return new Pageley_Gateway_Database_StandardQuerySqlDecorator(
			new Pageley_Query_Basic($type, $condition, $order, $range), $this->gateway);
	}

	public function testEmptyCondition() {
		$query = $this->newQuery($this->articleType);
		$this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
			. "FROM \"pageley_commerce\".\"article\" AS a1", $query->toSql());
	}

	public function testSingleCondition() {
		$query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("price", 2.5));
		$this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
			. "FROM \"pageley_commerce\".\"article\" AS a1 "
			. "WHERE a1.\"price\" = :price1", $query->toSql());
		$this->assertEquals(2.5, $query->getValues()->get("price1"));
	}

    public function testReferenceCondition() {
        $category = $this->categoryType->newInstance();
        $category->setId(2);
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("category", $category));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "WHERE a1.\"category_id\" = :category1", $query->toSql());
        $this->assertEquals(2, $query->getValues()->get("category1"));
    }

    public function testIdCondition() {
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("id", 1));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "WHERE a1.\"article_id\" = :id1", $query->toSql());
        $this->assertEquals(1, $query->getValues()->get("id1"));
    }

	public function testCount() {
		$query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("price", 2.5));
		$this->assertEquals("SELECT COUNT(*) AS \"count\" "
			. "FROM \"pageley_commerce\".\"article\" AS a1 "
			. "WHERE a1.\"price\" = :price1", $query->toSql(Pageley_Gateway_Database_QueryType::$COUNT));
		$this->assertEquals(2.5, $query->getValues()->get("price1"));
	}

    public function testAggregateCondition() {
        $category = $this->categoryType->newInstance();
        $category->setId(1);
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_AndGroup(
            new Pageley_Util_ArrayMap(
                new Pageley_Query_Condition_GreaterThan("price", 2.5),
                new Pageley_Query_Condition_LessThan("price", 5.0),
                new Pageley_Query_Condition_Equals("category", $category)
            )));
		$this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
			. "FROM \"pageley_commerce\".\"article\" AS a1 "
			. "WHERE ( a1.\"price\" > :price1 "
			. "AND a1.\"price\" < :price2 "
            . "AND a1.\"category_id\" = :category1 )", $query->toSql());
		$this->assertEquals(2.5, $query->getValues()->get("price1"));
		$this->assertEquals(5.0, $query->getValues()->get("price2"));
		$this->assertEquals(1, $query->getValues()->get("category1"));
    }

    public function testNotCondition() {
		$query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Not(
            new Pageley_Query_Condition_Equals("price", 2.5)));
		$this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
			. "FROM \"pageley_commerce\".\"article\" AS a1 "
			. "WHERE NOT a1.\"price\" = :price1", $query->toSql());
		$this->assertEquals(2.5, $query->getValues()->get("price1"));
    }
    
    public function testOrder() {
        $query = $this->newQuery($this->articleType, null, new Pageley_Util_ArrayMap(array(
                "price" => Pageley_Query_Direction::$DESCENDING,
                "name" => Pageley_Query_Direction::$ASCENDING
            )));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "ORDER BY a1.\"price\" DESC, a1.\"name\" ASC", $query->toSql());
    }
    
    public function testRange() {
        $query = $this->newQuery($this->articleType, null, null, new Pageley_Query_Range(6, 10));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "OFFSET 5 ROWS FETCH FIRST 5 ROWS ONLY", $query->toSql());
    }

    public function testEqualsNull() {
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("price", null));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "WHERE a1.\"price\" IS NULL", $query->toSql());
    }

    public function testNotEqualsNull() {
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_NotEquals("price", null));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "WHERE a1.\"price\" IS NOT NULL", $query->toSql());
    }

    public function testEagerFetch() {
        $query = $this->newQuery($this->eagerType);
        $this->assertEquals("SELECT e1.\"eager_id\", e1.\"category_id\", c1.\"category_id\" AS \"category_category_id\", c1.\"name\" AS \"category_name\", c1.\"parent_category_id\" AS \"category_parent_category_id\" "
            . "FROM \"pageley_commerce\".\"eager\" AS e1 "
            . "INNER JOIN \"pageley_commerce\".\"category\" AS c1 ON e1.\"category_id\" = c1.\"category_id\"", $query->toSql());
    }

    public function testOneToManyCondition() {
        $keyword = $this->keywordType->newInstance();
        $keyword->setId(3);
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("keywords", $keyword));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "INNER JOIN \"pageley_commerce\".\"keyword\" AS k1 ON a1.\"article_id\" = k1.\"article_id\" "
            . "WHERE k1.\"keyword_id\" = :keywords1", $query->toSql());
        $this->assertEquals(3, $query->getValues()->get("keywords1"));
    }

    public function testManyToManyCondition() {
        $tag = $this->tagType->newInstance();
        $tag->setId(5);
        $query = $this->newQuery($this->articleType, new Pageley_Query_Condition_Equals("tags", $tag));
        $this->assertEquals("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\", a1.\"category_id\" "
            . "FROM \"pageley_commerce\".\"article\" AS a1 "
            . "INNER JOIN \"pageley_commerce\".\"article_tag\" AS a2 ON a1.\"article_id\" = a2.\"article_id\" "
            . "WHERE a2.\"tag_id\" = :tags1", $query->toSql());
        $this->assertEquals(5, $query->getValues()->get("tags1"));
    }
}